package com.example.ssrbac.security;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * JWT Token工具类
 * 
 * 该类负责处理JWT token的生成、验证和解析等操作
 * 主要功能包括：
 * 1. 生成JWT token
 * 2. 从token中提取用户名
 * 3. 验证token的有效性
 * 4. 检查token是否过期
 * 5. 使token失效（黑名单机制）
 * 
 * @author Your Name
 * @version 1.0
 */
@Component
public class JwtTokenUtil {
    // 使用SLF4J进行日志记录
    private static final Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class);

    /**
     * JWT密钥，从配置文件中读取
     */
    @Value("${jwt.secret}")
    private String secret;

    /**
     * JWT过期时间（秒），从配置文件中读取
     */
    @Value("${jwt.expiration}")
    private Long expiration;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 为用户生成JWT token
     * 
     * @param userDetails 用户详情
     * @return 生成的JWT token
     */
    public String generateToken(UserDetails userDetails) {
        logger.debug("开始为用户 {} 生成token", userDetails.getUsername());
        Map<String, Object> claims = new HashMap<>();
        String token = createToken(claims, userDetails.getUsername());
        logger.debug("成功为用户 {} 生成token", userDetails.getUsername());
        return token;
    }

    /**
     * 创建JWT token
     * 
     * @param claims token中要包含的额外信息
     * @param subject token的主题（通常是用户名）
     * @return 生成的JWT token
     */
    private String createToken(Map<String, Object> claims, String subject) {
        logger.debug("创建token，主题: {}", subject);
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 从token中提取用户名
     * 
     * @param token JWT token
     * @return 用户名
     * @throws JwtException 如果token无效或已过期
     */
    public String getUsernameFromToken(String token) {
        logger.debug("从token中提取用户名");
        try {
            String username = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody()
                    .getSubject();
            logger.debug("成功从token中提取用户名: {}", username);
            return username;
        } catch (Exception e) {
            logger.error("从token中提取用户名失败", e);
            throw e;
        }
    }

    /**
     * 验证token的有效性
     * 
     * @param token JWT token
     * @param userDetails 用户详情
     * @return 如果token有效返回true，否则返回false
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        logger.debug("验证token的有效性");
        try {
            final String username = getUsernameFromToken(token);
            boolean isValid = username.equals(userDetails.getUsername()) && !isTokenExpired(token);
            logger.debug("token验证结果: {}", isValid);
            return isValid;
        } catch (Exception e) {
            logger.error("token验证失败", e);
            return false;
        }
    }

    /**
     * 检查token是否过期
     * 
     * @param token JWT token
     * @return 如果token已过期返回true，否则返回false
     */
    private boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        boolean isExpired = expiration.before(new Date());
        logger.debug("token过期检查结果: {}", isExpired);
        return isExpired;
    }

    /**
     * 从token中获取过期时间
     * 
     * @param token JWT token
     * @return 过期时间
     */
    private Date getExpirationDateFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody()
                .getExpiration();
    }

    /**
     * 使token失效
     * 注意：这是一个示例方法，实际实现需要配合Redis等缓存系统
     * 
     * @param token 需要失效的token
     */
    public void invalidateToken(String token) {
        logger.debug("使token失效: {}", token);
        try {
            // 获取token的过期时间
            Date expirationDate = getExpirationDateFromToken(token);
            // 计算剩余有效时间(毫秒)
            long timeToExpire = expirationDate.getTime() - System.currentTimeMillis();
            
            if (timeToExpire > 0) {
                // 将token加入Redis黑名单,过期时间设置为token的剩余有效期
                redisTemplate.opsForValue().set(
                    "blacklist:" + token,
                    "1",
                    timeToExpire,
                    TimeUnit.MILLISECONDS
                );
                logger.info("token已加入黑名单,将在{}毫秒后过期", timeToExpire);
            } else {
                logger.info("token已过期,无需加入黑名单");
            }
        } catch (Exception e) {
            logger.error("使token失效时发生错误", e);
            throw new RuntimeException("使token失效失败", e);
        }
    }
} 